import { HubConnectionBuilder, LogLevel } from '@microsoft/signalr';

/**
 * SignalR 服务封装
 *
 * 用法示例:
 * const svc = new SignalRService({
 *   hubUrl: '/hubs/plant',
 *   getAccessToken: () => localStorage.getItem('access_token'),
 *   reconnectOptions: { maxRetries: 10, baseDelayMs: 2000 }
 * });
 *
 * await svc.start();
 * svc.on('ReceiveMessage', msg => console.log(msg));
 * await svc.invoke('SendMessage', 'hello');
 * await svc.stop();
 */

export default class SignalRService {
  /**
   * @param {Object} options
   * @param {string} options.hubUrl - SignalR Hub URL (relative or absolute)
   * @param {function(): string|Promise<string>} [options.getAccessToken] - 返回当前访问令牌的函数
   * @param {Object} [options.reconnectOptions]
   * @param {number} [options.reconnectOptions.maxRetries=Infinity]
   * @param {number} [options.reconnectOptions.baseDelayMs=2000]
   * @param {boolean} [options.log=false]
   */
  constructor({
    hubUrl,
    getAccessToken = null,
    reconnectOptions = {},
    log = false
  } = {}) {
    if (!hubUrl) throw new Error('hubUrl is required');

    this.hubUrl = hubUrl;
    this.getAccessToken = getAccessToken;
    this.log = log;

    this.reconnectOptions = {
      maxRetries: reconnectOptions.maxRetries ?? Infinity,
      baseDelayMs: reconnectOptions.baseDelayMs ?? 2000
    };

    this._connection = this._buildConnection();
    this._isStopping = false;
    this._retryCount = 0;
    this._reconnectTimer = null;

    // 记录注册的事件，方便卸载
    this._handlers = new Map();
  }

  _buildConnection() {
    const builder = new HubConnectionBuilder()
      .withUrl(this.hubUrl, this.getAccessToken ? {
        accessTokenFactory: () => {
          try {
            const tokenOrPromise = this.getAccessToken();
            return tokenOrPromise;
          } catch (e) {
            // 保证返回 undefined 而不是抛出
            return undefined;
          }
        }
      } : undefined)
      .withAutomaticReconnect() // keep built-in transient reconnect behavior as a base
      .configureLogging(this.log ? LogLevel.Information : LogLevel.Error);

    return builder.build();
  }

  get isConnected() {
    return this._connection && this._connection.state === 'Connected';
  }

  async start() {
    this._isStopping = false;

    if (this.isConnected) {
      return;
    }

    // 如果已有实例已被关闭（例如 stop 后），重新创建连接实例
    if (!this._connection) {
      this._connection = this._buildConnection();
    }

    // 将 onclose 绑定一次
    this._connection.onclose(async (error) => {
      if (this.log) console.warn('SignalR connection closed', error);
      if (this._isStopping) return;
      this._scheduleReconnect();
    });

    try {
      await this._connection.start();
      this._retryCount = 0;
      if (this.log) console.info('SignalR connected');
      // 恢复已注册的事件处理器（builder.withAutomaticReconnect 可能已恢复内部状态，但我们确保 handler 完整）
      this._reapplyHandlers();
    } catch (err) {
      if (this.log) console.error('SignalR start failed', err);
      this._scheduleReconnect();
      throw err;
    }
  }

  async stop() {
    this._isStopping = true;
    if (this._reconnectTimer) {
      clearTimeout(this._reconnectTimer);
      this._reconnectTimer = null;
    }
    if (this._connection) {
      try {
        await this._connection.stop();
      } catch (err) {
        if (this.log) console.warn('SignalR stop error', err);
      } finally {
        this._connection = null;
      }
    }
  }

  _scheduleReconnect() {
    if (this._reconnectTimer) return;

    if (this._retryCount >= this.reconnectOptions.maxRetries) {
      if (this.log) console.warn('SignalR max reconnect attempts reached');
      return;
    }

    const attempt = ++this._retryCount;
    const base = this.reconnectOptions.baseDelayMs;
    // 指数退避 + 随机抖动
    const delay = Math.min(base * Math.pow(2, attempt - 1), 60000) + Math.floor(Math.random() * 1000);

    if (this.log) console.info(`Scheduling SignalR reconnect attempt ${attempt} in ${delay}ms`);

    this._reconnectTimer = setTimeout(async () => {
      this._reconnectTimer = null;
      try {
        // rebuild connection to ensure fresh token factory usage
        this._connection = this._buildConnection();
        // rebind onclose
        this._connection.onclose((error) => {
          if (this.log) console.warn('SignalR connection closed', error);
          if (this._isStopping) return;
          this._scheduleReconnect();
        });
        await this._connection.start();
        this._retryCount = 0;
        if (this.log) console.info('SignalR reconnected');
        this._reapplyHandlers();
      } catch (err) {
        if (this.log) console.error('SignalR reconnect failed', err);
        this._scheduleReconnect();
      }
    }, delay);
  }

  // 重新应用已注册的事件处理器
  _reapplyHandlers() {
    if (!this._connection) return;
    for (const [method, handlers] of this._handlers.entries()) {
      // 先移除所有以前的绑定（避免重复）
      try {
        this._connection.off(method);
      } catch (e) {
        // ignore
      }
      for (const handler of handlers) {
        this._connection.on(method, handler);
      }
    }
  }

  /**
   * 注册事件
   * @param {string} method
   * @param {function} handler
   */
  on(method, handler) {
    if (!method || typeof handler !== 'function') return;
    const list = this._handlers.get(method) || [];
    list.push(handler);
    this._handlers.set(method, list);

    if (this._connection) {
      this._connection.on(method, handler);
    }
  }

  /**
   * 注销事件
   * @param {string} method
   * @param {function} [handler] - 不传 handler 将移除该方法下所有处理器
   */
  off(method, handler) {
    if (!method) return;
    const list = this._handlers.get(method);
    if (!list) return;

    if (handler) {
      const idx = list.indexOf(handler);
      if (idx >= 0) list.splice(idx, 1);
    } else {
      list.length = 0;
    }

    if (list.length === 0) {
      this._handlers.delete(method);
    } else {
      this._handlers.set(method, list);
    }

    if (this._connection) {
      try {
        if (handler) this._connection.off(method, handler);
        else this._connection.off(method);
      } catch (e) {
        // ignore
      }
    }
  }

  /**
   * 发送但不等待结果（fire-and-forget）
   * @param {string} method
   * @param  {...any} args
   */
  async send(method, ...args) {
    if (!this._connection) throw new Error('SignalR connection not initialized');
    return this._connection.send(method, ...args);
  }

  /**
   * 调用并等待返回（invoke）
   * @param {string} method
   * @param  {...any} args
   */
  async invoke(method, ...args) {
    if (!this._connection) throw new Error('SignalR connection not initialized');
    return this._connection.invoke(method, ...args);
  }
}

/**
 * 辅助工厂：创建并自动启动服务
 * @param {Object} options - 见 SignalRService.constructor
 * @returns {Promise<SignalRService>}
 */
export async function createAndStart(options) {
  const svc = new SignalRService(options);
  await svc.start();
  return svc;
}
